import  * as path from "path"
import  * as fs from "fs"
import {Express} from "express";
import {Request} from "express";
import {Response} from "express";
import {sdf, MapString} from "./public/sdf";
import {Menu} from "./Menu";
import {RouterPara, ExpFunc} from "./ExpFunc";
import {Log} from "./Log";
import {HotReload} from "./HotReload";


export class ExpressExt {

    static allRoute: ExpressExt[] = []

    private funcMap: MapString<(req: Request, resp: Response)=>any> = {};

    /**
     * 每个路由加载回调
     * @param url
     * @param modReq
     * @param resp
     * @param method
     */
    static routeUrl = function (url: string|null, modReq: { new(): any;}|null, resp: any, method: string|null) {

    }


    /**
     * 处理跨域请求
     * @param req
     * @param resp
     * @param next
     */
    static cros(req: Request, resp: Response, next: ()=> void) {
        var oneof = false;
        if (req.headers["origin"]) {
            resp.header('Access-Control-Allow-Origin', "*");
            oneof = true;
        }
        if (req.headers['access-control-request-method']) {
            resp.header('Access-Control-Allow-Methods', req.headers['access-control-request-method']);
            oneof = true;
        }
        if (req.headers['access-control-request-headers']) {
            resp.header('Access-Control-Allow-Headers', req.headers['access-control-request-headers']);
            oneof = true;
        }
        if (oneof) {
            resp.header('Access-Control-Max-Age', (60 * 60 * 24 * 365) + "");
        }

        // intercept OPTIONS method
        if (oneof && req.method == 'OPTIONS') {
            resp.sendStatus(200);
        }
        else {
            next();
        }
    }

    /**
     * 加载路由文件
     * @param http Express
     * @param menu 路由文件目录,以及异常捕获函数
     */
    static loadRoutes(http: Express, menu: RouterPara[]) {
        for (let m of menu) {
            var routesPath = path.join(Menu.root, m.menu);

            console.log("load Routes :" + routesPath);

            let ExpEx = new ExpressExt(http, m);
            ExpressExt.allRoute.push(ExpEx);

            fs.readdirSync(routesPath).forEach(function (file) {
                if (file.endsWith(".js")) {
                    var routePath = path.join(routesPath, file);
                    // self.logger.info(routePath);
                    try {
                        require(routePath)(ExpEx, m.extPara);
                    } catch (e) {
                        Log.err(e);
                    }
                }
            });
        }

        ExpressExt.routeUrl(null, null, "", null);
    }

    static reloadRoutes() {
        console.error("热更新");
        for (let m of ExpressExt.allRoute) {
            var routesPath = path.join(Menu.root, m.router.menu);
            fs.readdirSync(routesPath).forEach(function (file) {

                if (file.endsWith(".js")) {
                    var routePath = path.join(routesPath, file);
                    // self.logger.info(routePath);
                    try {
                        HotReload.cleanCache(routePath);
                        require(routePath)(m, m.router.extPara);
                    } catch (e) {
                        // Log.err(e);
                    }
                }
            });
        }
    }

    static setReqParas(req: Request) {
        if (req.method === "POST") {
            sdf.joinObjFast(req.body, req.query)
            sdf.joinObjFast(req.body, req.params);
            return req.body;
        }
        else {
            sdf.joinObjFast(req.query, req.params);
            return req.query;
        }
    }


    constructor(public express: Express, public router: RouterPara) {
        if (!this.router.onCatch) {
            this.router.onCatch = ExpFunc.onCatch;
        }

        if (!this.router.onCheckData) {
            this.router.onCheckData = ExpFunc.onCheckData;
        }

        if (!this.router.onSend) {
            this.router.onSend = ExpFunc.onSend;
        }

    }


    /**
     * 添加post接口
     * @param url
     * @param modReq 接口请求参数
     * @param res
     * @param respData 用于生成接口文档,注明返回值类型
     */
    post<T>(url: string,
            modReq: { new(): T ;}|null,
            res: (req: Request, res: Response, data?: T)=>(Promise<any>|Object|void),
            respData?: any) {
        this.addMethod("post", url, modReq, res, respData);
    }

    /**
     * 所有接口请求
     * @param url
     * @param modReq
     * @param res
     * @param respData
     */
    all<T>(url: string,
           modReq: { new(): T ;}|null,
           res: (req: Request, res: Response, data: T)=>(Promise<any>|Object|void),
           respData?: any) {
        this.addMethod("all", url, modReq, res, respData);
    }

    put<T>(url: string,
           modReq: { new(): T ;}|null,
           res: (req: Request, res: Response, data: T)=>(Promise<any>|Object|void),
           respData?: any) {
        this.addMethod("put", url, modReq, res, respData);
    }

    delete<T>(url: string,
              modReq: { new(): T ;}|null,
              res: (req: Request, res: Response, data: T)=>(Promise<any>|Object|void),
              respData?: any) {
        this.addMethod("delete", url, modReq, res, respData);
    }

    get<T>(url: string,
           modReq: { new(): T ;}|null,
           res: (req: Request, res: Response, data: T)=>(Promise<any>|Object|void),
           respData?: any) {
        this.addMethod("get", url, modReq, res, respData);
    }

    private addMethod<T>(method: "get"|"post"|"all"|"put"|"delete",
                         url: string,
                         modReq: { new(): T ;}|null,
                         res: (req: Request, res: Response, data: T)=>(Promise<any>|Object|void),
                         respData?: any) {

        let old = this.funcMap[url];

        this.funcMap[url] = (req: Request, resp: Response)=> {
            try {
                let ret = this.router.onCheckData(url, modReq, req, resp, (data)=> {
                    try {
                        let promi = res(req, resp, data) as Promise<any>;
                        if (promi instanceof Promise) {
                            promi.then(r=> {
                                if (r !== void 0) {
                                    this.router.onSend!(url, modReq, req, resp, r);
                                    // resp.send(r);
                                }
                            }).catch(e=> {
                                this.router.onCatch(url, modReq, req, resp, e);
                            });
                        }
                        else if (promi !== void 0) {
                            this.router.onSend!(url, modReq, req, resp, promi);
                            // resp.send(promi);
                        }
                    } catch (e) {
                        this.router.onCatch(url, modReq, req, resp, e);
                    }
                }, this.router);

                if (ret instanceof Promise) {
                    ret.catch(e=> {
                        this.router.onCatch(url, modReq, req, resp, e);
                    });
                }

            } catch (e) {
                this.router.onCatch(url, modReq, req, resp, e);
            }
        }

        if (old)
            return;

        ExpressExt.routeUrl(url, modReq, respData, method);

        this.express[method](url, (req: Request, resp: Response)=> this.funcMap[url](req, resp));
    }


}